﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Resources;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using System.Text;

using Microsoft.BizTalk.Bam.EventObservation;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Streaming;
using Microsoft.BizTalk.XPath;

using Software41.Tracking.BAM;
using Software41.Tracking.BAM.Adapters;

namespace Software41.TrackingExample.PipelineComponents
{
    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [System.Runtime.InteropServices.Guid("A7A125E2-07A0-4b31-8D87-8F0D1E9172B7")]
    [ComponentCategory(CategoryTypes.CATID_Any)]
    public class StartProcessWithXpath :
                           IBaseComponent,
                            IComponentUI,
                            Microsoft.BizTalk.Component.Interop.IComponent,
                            IPersistPropertyBag
    {
        #region Private Properties
        private string _processId = String.Empty;
        private bool _allowEndStep = false;
        

        private ResourceManager resourceManager =
            new ResourceManager("Software41.TrackingExample.PipelineComponents.StartProcessWithXpath", Assembly.GetExecutingAssembly());
        #endregion

        #region Public Properties
        private bool _startProcess = false;
        public bool StartProcess
        {
            get { return _startProcess; }
            set { _startProcess = value; }
        }

        private bool _startStep = false;
        public bool StartStep
        {
            get { return _startStep; }
            set { _startStep = value; }
        }

        private XPathPropertiesCollection _xPathProperties = new XPathPropertiesCollection();
        public XPathPropertiesCollection XPathPropertiesCollection
        {
            get { return this._xPathProperties; }
            set { this._xPathProperties = value; }
        }
        #endregion

        #region IBaseComponent
        // <summary>
        /// Name of the component
        /// </summary>
        [Browsable(false)]
        public string Name
        {
            get { return resourceManager.GetString("COMPONENTNAME", System.Globalization.CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Version of the component
        /// </summary>
        [Browsable(false)]
        public string Version
        {
            get { return resourceManager.GetString("COMPONENTVERSION", System.Globalization.CultureInfo.InvariantCulture); }
        }
        /// <summary>
        /// Description of the component
        /// </summary>
        [Browsable(false)]
        public string Description
        {
            get
            {
                return this.resourceManager.GetString("COMPONENTDESCRIPTION", System.Globalization.CultureInfo.InvariantCulture);
            }
        }
        #endregion

        #region IComponentUI
        [Browsable(false)]
        public IntPtr Icon
        {
            get
            {
                return ((System.Drawing.Bitmap)(this.resourceManager.GetObject("COMPONENTICON", System.Globalization.CultureInfo.InvariantCulture))).GetHicon();
            }
        }


        public System.Collections.IEnumerator Validate(object projectSystem)
        {
            return null;
        }
        #endregion

        #region IPersistPropertyBag
        public void GetClassID(out Guid classID)
        {
            classID = new Guid("12C1E4F0-BB4E-4fd2-8A37-746CAF8DDEB7");
        }

        public void InitNew()
        {
            throw new NotImplementedException();
        }

        public void Load(IPropertyBag propertyBag, int errorLog)
        {
            object val = this.ReadPropertyBag(propertyBag, "XPathPropertiesCollection");

            if (val != null)
            {
                string xPathPropsList = (string)val;

                XmlTextReader xml = new XmlTextReader(new StringReader(xPathPropsList));
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.IgnoreProcessingInstructions = true;
                XmlReader reader = XmlReader.Create(xml, settings);

                XmlSerializer ser = new XmlSerializer(typeof(XPathPropertiesCollection));
                XPathPropertiesCollection props = (XPathPropertiesCollection)ser.Deserialize(reader);
                this.XPathPropertiesCollection = props;
            }

            val = this.ReadPropertyBag(propertyBag, "StartProcess");

            if (val != null)
            {
                this.StartProcess = (bool)val;
            }

            val = this.ReadPropertyBag(propertyBag, "StartStep");

            if (val != null)
            {
                this.StartStep = (bool)val;
            }
        }

        public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
        {
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);

            XmlWriterSettings setting = new XmlWriterSettings();
            setting.OmitXmlDeclaration = true;
            XmlWriter writer = XmlWriter.Create(sw, setting);

            XmlSerializer ser = new XmlSerializer(typeof(XPathPropertiesCollection));
            ser.Serialize(writer, this.XPathPropertiesCollection);

            object val = sb.ToString();

            this.WritePropertyBag(propertyBag, "XPathPropertiesCollection", val);
            this.WritePropertyBag(propertyBag, "StartProcess", this.StartProcess);
            this.WritePropertyBag(propertyBag, "StartStep", this.StartStep);
        }
        #endregion

        #region IComponent
        public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
        {
            ProcessTraceInfo process = null;
            IEventStream eventStream = null;

            try
            {
                string contextNamespace = "http://Software41.TrackingExample.CtxProps";

                //Don't do anything if the client doesn't want to start process
                if (this.StartProcess)
                {
                    string processName = String.Empty;
                    string processId = String.Empty;
                    string stepName = String.Empty;
                    string stepId = Guid.NewGuid().ToString();

                    XPathReader rdr = new XPathReader();

                    IBaseMessagePart bodyPart = pInMsg.BodyPart;

                    Stream inboundStream = bodyPart.GetOriginalDataStream();
                    VirtualStream virtualStream = new VirtualStream();
                    ReadOnlySeekableStream readOnlySeekableStream = new ReadOnlySeekableStream(inboundStream, virtualStream);
                    XmlTextReader xmlTextReader = new XmlTextReader(readOnlySeekableStream);
                    XPathCollection collection = new XPathCollection();

                    foreach (var xPathProps in this.XPathPropertiesCollection)
                    {
                        collection.Add(((XPathProperties)xPathProps).XPathExpr);
                    }

                    XPathReader xPathReader = new XPathReader(xmlTextReader, collection);

                    while (xPathReader.ReadUntilMatch())
                    {
                        switch (xPathReader.Name.ToUpper())
                        {
                            case "PROCESSID":
                                processId = xPathReader.ReadString();

                                if (processId.Trim().Equals(String.Empty))
                                {
                                    processId = Guid.NewGuid().ToString();
                                }
                                break;
                            case "PROCESSNAME":
                                processName = xPathReader.ReadString();

                                if (processName.Trim().Equals(String.Empty))
                                {
                                    processName = "StartProcessWithXpath";
                                }
                                break;
                            case "STEPNAME":
                                stepName = xPathReader.ReadString();

                                if (stepName.Trim().Equals(String.Empty))
                                {
                                    stepName = "StartStepWithXpath";
                                }
                                break;
                        }
                    }

                    var contextStream = pContext.GetEventStream();
                    eventStream = new EventStreamAdapter(contextStream);

                    //begin the process with the passed in name
                    process = new ProcessTraceInfo(processName,processId, eventStream);

                    if (process != null)
                    {
                        //create properties and move on
                        string messageData = String.Empty;
                        readOnlySeekableStream.Position = 0;
                        XmlTextReader reader = new XmlTextReader(readOnlySeekableStream);
                        if(reader.Read())
                        {
                            messageData = reader.ReadOuterXml();
                        }
                        
                        if (messageData.Length == 0)
                        {
                            messageData = "<Data>No MessageData Returned.</Data>";
                        }

                        process.Begin(processName, messageData);

                        var logDetail = process.AddLogDetail();
                        logDetail.AddDetail("Beging Process in StartProcessWithXpath component.");

                        #region Add Context Properties
                        //add context properties for downstream client
                        pInMsg.Context.Write("ProcessId", contextNamespace, processId);
                        pInMsg.Context.Write("ProcessName", contextNamespace, processName);
                        pInMsg.Context.Write("IsProcessActive", contextNamespace, true);

                        if (this._startStep)
                        {
                            var step = new StepInfo(stepName, stepId, process.ProcessInfoId, eventStream);
                            step.Begin("MessageData", messageData);

                            logDetail.AddDetail("Beging Step in StartProcessWithXpath component.");

                            pInMsg.Context.Write("StepName", contextNamespace, stepName);
                            pInMsg.Context.Write("StepInfoId", contextNamespace, stepId);
                            pInMsg.Context.Write("IsStepActive", contextNamespace, true);
                        }
                        else
                        {
                            pInMsg.Context.Write("IsStepActive", contextNamespace, false);
                        }
                        #endregion

                        //stuff it back in
                        readOnlySeekableStream.Position = 0;
                        bodyPart.Data = readOnlySeekableStream;

                        if (logDetail != null)
                        {
                            logDetail.End();
                        }
                        eventStream.Flush();
                    }
                    else
                    {
                        //null ref so no active process
                        pInMsg.Context.Write("IsProcessActive", contextNamespace, false);
                        pInMsg.Context.Write("IsStepActive", contextNamespace, false);
                    }
                }
                else
                {
                    //client did not want to begin process
                    pInMsg.Context.Write("IsProcessActive", contextNamespace, false);
                    pInMsg.Context.Write("IsStepActive", contextNamespace, false);
                }
            }
            catch (Exception ex)
            {
                if (process != null)
                {
                    process.AddException(ex);
                    process.End(false);
                    eventStream.Flush();
                }
            }
            return pInMsg;
        }
        #endregion

        #region utility functionality
        /// <summary> 
        /// Reads property value from property bag 
        /// </summary> 
        /// <param name="pb">Property bag</param> 
        /// <param name="propName">Name of property</param> 
        /// <returns>Value of the property</returns> 
        private object ReadPropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName)
        {
            object val = null;
            try
            {
                pb.Read(propName, out val, 0);
            }
            catch (System.ArgumentException)
            {
                return val;
            }
            catch (System.Exception e)
            {
                throw new System.ApplicationException(e.Message);
            }
            return val;
        }

        /// <summary> 
        /// Writes property values into a property bag. 
        /// </summary> 
        /// <param name="pb">Property bag.</param> 
        /// <param name="propName">Name of property.</param> 
        /// <param name="val">Value of property.</param> 
        private void WritePropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, object val)
        {
            try
            {
                pb.Write(propName, ref val);
            }
            catch (System.Exception e)
            {
                throw new System.ApplicationException(e.Message);
            }
        }
        #endregion 

    }
}
